package com.sdnu.iosclub.qvs.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.transaction.RabbitTransactionManager;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

/**
 * @Author: Zhao YunLai
 * @Date: 2022/09/22/21:36
 * @Description:
 */
@Configuration
@Slf4j
public class RabbitMQConfig implements InitializingBean {
    public static String QVS_EXCHANGE_NAME;
    public static String QVS_RESULT_QUEUE;
    public static String QVS_TEXT_QUEUE;
    public static String QVS_RESULT_ROUTING;
    public static String QVS_TEXT_ROUTING;


    @Value("${mq.exchange.name}")
    private String qvsExchangeName;

    @Value("${mq.queue.result}")
    private String qvsResultQueueName;

    @Value("${mq.queue.text}")
    private String qvsTextQueueName;

    @Value("${mq.routing.result}")
    private String qvsResultRouting;

    @Value("${mq.routing.text}")
    private String qvsTextRouting;

    @Override
    public void afterPropertiesSet() throws Exception {
        QVS_EXCHANGE_NAME = qvsExchangeName;
        QVS_RESULT_QUEUE = qvsResultQueueName;
        QVS_TEXT_QUEUE = qvsTextQueueName;
        QVS_RESULT_ROUTING = qvsResultRouting;
        QVS_TEXT_ROUTING = qvsTextRouting;
    }

    /**
     * 声明交换机
     */
    @Bean("qvsExchange")
    public DirectExchange qvsExchange(){
        return new DirectExchange(QVS_EXCHANGE_NAME);
    }

    /**
     * 声明队列
     */
    @Bean("qvsResultQueue")
    public Queue qvsResultQueue(){
        return QueueBuilder.durable(QVS_RESULT_QUEUE).build();
    }
    @Bean("qvsTextQueue")
    public Queue qvsTextQueue(){
        return QueueBuilder.durable(QVS_TEXT_QUEUE).build();
    }

    /**
     * 选择题结果队列绑定交换机
     * @param qvsQueue 队列bean对象
     * @param qvsExchange 交换机bean对象
     * @return 绑定对象
     */
    @Bean
    public Binding qvsResultQueueBinding(@Qualifier("qvsResultQueue") Queue qvsQueue,
                                        @Qualifier("qvsExchange") DirectExchange qvsExchange){

        return BindingBuilder.bind(qvsQueue).to(qvsExchange).with(QVS_RESULT_ROUTING);
    }
    @Bean
    public Binding qvsTextQueueBinding(@Qualifier("qvsTextQueue") Queue qvsQueue,
                                         @Qualifier("qvsExchange") DirectExchange qvsExchange){

        return BindingBuilder.bind(qvsQueue).to(qvsExchange).with(QVS_TEXT_ROUTING);
    }




    ///**
    // * 定义消息转换实例 ，转化成 JSON传输
    // *
    // * @return Jackson2JsonMessageConverter
    // */
    //@Bean
    //public MessageConverter integrationEventMessageConverter() {
    //    return new Jackson2JsonMessageConverter();
    //}
    //
    /**
     * 配置启用rabbitmq事务
     *
     * @param connectionFactory connectionFactory
     * @return RabbitTransactionManager
     */
    @Bean
    public RabbitTransactionManager rabbitTransactionManager(CachingConnectionFactory connectionFactory) {
        return new RabbitTransactionManager(connectionFactory);
    }

    @Autowired
    RabbitTemplate rabbitTemplate;


    //在 依赖注入（@Autowired等）之后会被自动调用
    // @PostConstruct注解的方法将会在依赖注入完成后被自动调用。
    @PostConstruct
    private void init(){

        /**
         * 交换机不管是否收到消息都会进行的一个回调方法，
         * 需要在配置文件中开启确认接收的功能
         * @param correlationData 保存回调信息的Id及相关信息
         * @param b 交换机收到消息 为true
         * @param s 未收到消息的原因
         */
        rabbitTemplate.setConfirmCallback((CorrelationData correlationData, boolean b, String s)->{
            if (!b) {
                log.warn("消息未确认 cause:{} - correlationData:{}",s,correlationData);
            }
        });

        /**
         * 通过实现ReturnCallback接口，
         * 如果消息从交换器发送到对应队列失败时触发
         * 比如根据发送消息时指定的routingKey找不到队列时会触发
         * 在配置文件中设置 publisher-returns: true
         *
         * * 当消息无法路由的时候的回调方法
         * * message 消息
         * * replyCode 编码
         * * replyText 退回原因
         * * exchange 从哪个交换机退回
         * *
         */
        rabbitTemplate.setReturnCallback((Message message, int replyCode, String replyText, String exchange, String routingKey)->{
            log.error("消息{},被交换机{}退回", message,exchange);
            log.error("消息使用的路由键:{},退回原因:{}", routingKey,replyText);
        });
    }
}
